package com.maodou.v2ray.util

import android.content.Context
import android.text.TextUtils
import android.util.Log
import com.google.gson.Gson
import com.maodou.v2ray.dto.AppConfig
import com.maodou.v2ray.dto.ERoutingMode
import com.maodou.v2ray.dto.V2rayConfig
import com.maodou.v2ray.util.Utils
import kotlin.collections.get
import kotlin.text.clear


object V2rayConfigUtil {


    data class Result(var status: Boolean, var content: String)


    /**
     * 生成v2ray的客户端配置文件
     */
    private fun getV2rayNonCustomConfig(
        context: Context,
        outbound: V2rayConfig.OutboundBean,
        remarks: String,
    ): Result {
        val result = Result(false, "")
        //取得默认配置
        val assets = Utils.readTextFromAssets(context, "v2ray_config.json")
        if (TextUtils.isEmpty(assets)) {
            return result
        }

        //转成Json
        val v2rayConfig = Gson().fromJson(assets, V2rayConfig::class.java) ?: return result

        v2rayConfig.log.loglevel = "debug"

        v2rayConfig.outbounds[0] = outbound

        routing(v2rayConfig)

        dns(v2rayConfig)

        customLocalDns(v2rayConfig)

        v2rayConfig.remarks = remarks

        result.status = true
        result.content = v2rayConfig.toPrettyPrinting()
        return result
    }




    /**
     * routing
     */
    private fun routing(v2rayConfig: V2rayConfig): Boolean {
        try {


            v2rayConfig.routing.domainStrategy =
                 "IPIfNonMatch"
//            v2rayConfig.routing.domainMatcher = "mph"
            val routingMode = ERoutingMode.BYPASS_LAN_MAINLAND.value

            // Hardcode googleapis.cn gstatic.com
            val googleapisRoute = V2rayConfig.RoutingBean.RulesBean(
                outboundTag = AppConfig.TAG_PROXY,
                domain = arrayListOf("domain:googleapis.cn", "domain:gstatic.com")
            )

            when (routingMode) {
                ERoutingMode.BYPASS_LAN.value -> {
                    routingGeo("ip", "private", AppConfig.TAG_DIRECT, v2rayConfig)
                }

                ERoutingMode.BYPASS_MAINLAND.value -> {
                    routingGeo("", "cn", AppConfig.TAG_DIRECT, v2rayConfig)
                    routingGeo("domain", "geolocation-cn", AppConfig.TAG_DIRECT, v2rayConfig)
                    v2rayConfig.routing.rules.add(0, googleapisRoute)
                }

                ERoutingMode.BYPASS_LAN_MAINLAND.value -> {
                    routingGeo("ip", "private", AppConfig.TAG_DIRECT, v2rayConfig)
                    routingGeo("", "cn", AppConfig.TAG_DIRECT, v2rayConfig)
                    routingGeo("domain", "geolocation-cn", AppConfig.TAG_DIRECT, v2rayConfig)
                    v2rayConfig.routing.rules.add(0, googleapisRoute)
                }

                ERoutingMode.GLOBAL_DIRECT.value -> {
                    val globalDirect = V2rayConfig.RoutingBean.RulesBean(
                        outboundTag = AppConfig.TAG_DIRECT,
                        port = "0-65535"
                    )
                    v2rayConfig.routing.rules.add(globalDirect)
                }
            }

            if(routingMode != ERoutingMode.GLOBAL_DIRECT.value) {
                v2rayConfig.routing.rules.add(
                    V2rayConfig.RoutingBean.RulesBean(
                        outboundTag = AppConfig.TAG_PROXY,
                        port = "0-65535"
                    ))
            }

        } catch (e: Exception) {
            e.printStackTrace()
            return false
        }
        return true
    }

    private fun routingGeo(
        ipOrDomain: String,
        code: String,
        tag: String,
        v2rayConfig: V2rayConfig
    ) {
        try {
            if (!TextUtils.isEmpty(code)) {
                //IP
                if (ipOrDomain == "ip" || ipOrDomain == "") {
                    val rulesIP = V2rayConfig.RoutingBean.RulesBean()
                    rulesIP.outboundTag = tag
                    rulesIP.ip = ArrayList()
                    rulesIP.ip?.add("geoip:$code")
                    v2rayConfig.routing.rules.add(rulesIP)
                }

                if (ipOrDomain == "domain" || ipOrDomain == "") {
                    //Domain
                    val rulesDomain = V2rayConfig.RoutingBean.RulesBean()
                    rulesDomain.outboundTag = tag
                    rulesDomain.domain = ArrayList()
                    rulesDomain.domain?.add("geosite:$code")
                    v2rayConfig.routing.rules.add(rulesDomain)
                }
            }
        } catch (e: Exception) {
            e.printStackTrace()
        }
    }

    private fun routingUserRule(userRule: String, tag: String, v2rayConfig: V2rayConfig) {
        try {
            if (!TextUtils.isEmpty(userRule)) {
                //Domain
                val rulesDomain = V2rayConfig.RoutingBean.RulesBean()
                rulesDomain.outboundTag = tag
                rulesDomain.domain = ArrayList()

                //IP
                val rulesIP = V2rayConfig.RoutingBean.RulesBean()
                rulesIP.outboundTag = tag
                rulesIP.ip = ArrayList()

                userRule.split(",").map { it.trim() }.forEach {
                    if (it.startsWith("ext:") && it.contains("geoip")) {
                        rulesIP.ip?.add(it)
                    } else if (Utils.isIpAddress(it) || it.startsWith("geoip:")) {
                        rulesIP.ip?.add(it)
                    } else if (it.isNotEmpty()) {
                        rulesDomain.domain?.add(it)
                    }
                }
                if ((rulesDomain.domain?.size ?: 0) > 0) {
                    v2rayConfig.routing.rules.add(rulesDomain)
                }
                if ((rulesIP.ip?.size ?: 0) > 0) {
                    v2rayConfig.routing.rules.add(rulesIP)
                }
            }
        } catch (e: Exception) {
            e.printStackTrace()
        }
    }

    private fun userRule2Domian(userRule: String): ArrayList<String> {
        val domain = ArrayList<String>()
        userRule.split(",").map { it.trim() }.forEach {
            if (it.startsWith("geosite:") || it.startsWith("domain:")) {
                domain.add(it)
            }
        }
        return domain
    }

    /**
     * Custom Dns
     */
    private fun customLocalDns(v2rayConfig: V2rayConfig): Boolean {
        try {

            // DNS outbound对象
            if (v2rayConfig.outbounds.none { e -> e.protocol == "dns" && e.tag == "dns-out" }) {
                v2rayConfig.outbounds.add(
                    V2rayConfig.OutboundBean(
                        protocol = "dns",
                        tag = "dns-out",
                        settings = V2rayConfig.OutboundBean.OutSettingsBean(),
                        streamSettings = null,
                        mux = null
                    )
                )
            }

            // DNS routing tag
            //53,853端口走内部DNS，除了阿里云DNS 223.5.5.5
            v2rayConfig.routing.rules.add(
                0 //放在第一个位置
                    , V2rayConfig.RoutingBean.RulesBean(
                    port = "53,853",
                    outboundTag = "dns-out",
                    domain = null
                )
            )

            //阿里云DNS直连，必须放在第一个路由
            v2rayConfig.routing.rules.add(
                0, V2rayConfig.RoutingBean.RulesBean(
                    outboundTag = AppConfig.TAG_DIRECT,
                    ip = arrayListOf(AppConfig.DNS_DIRECT)
                )
            )

        } catch (e: Exception) {
            e.printStackTrace()
            return false
        }
        return true
    }

    private fun dns(v2rayConfig: V2rayConfig): Boolean {
        try {
            val hosts = mutableMapOf<String, String>()
            val servers = ArrayList<Any>()

            //国内域名，使用国内的DNS服务器
            val geositeCn = arrayListOf("geosite:cn", "geosite:geolocation-cn")
            val geoipCn = arrayListOf("geoip:cn")
            servers.add(
                V2rayConfig.DnsBean.ServersBean(
                    AppConfig.DNS_DIRECT,
                    53,
                    geositeCn,
                    geoipCn
                )
            )

            //国外的域名都走 https://cloudflare-dns.com/dns-que
            //使用Dns over https 防止住宅ip不支持udp协议
            servers.add("https://cloudflare-dns.com/dns-query")

            // DNS dns对象
            v2rayConfig.dns = V2rayConfig.DnsBean(
                servers = servers,
                hosts = hosts
            )
        } catch (e: Exception) {
            e.printStackTrace()
            return false
        }
        return true
    }

}
